////////////////////////////////////////////////////////////////////////////////
//
//  FLEX USER GROUP HOKURIKU
//  Copyright 2009 Flex User Group Hokuriku
//  All Rights Reserved.
// 
//  NOTICE: Flex User Group Hokuriku permits you to use, 
//  modify, and distribute this file 
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package net.fxug.hokuriku.chocolat.service
{
    import com.adobe.errors.IllegalStateError;

    import flash.errors.IllegalOperationError;
    import flash.events.EventDispatcher;
    import flash.utils.Dictionary;

    import mx.logging.ILogger;

    import net.fxug.hokuriku.chocolat.dao.DatabaseManager;
    import net.fxug.hokuriku.chocolat.dao.VoteDao;
    import net.fxug.hokuriku.chocolat.events.VoteEvent;
    import net.fxug.hokuriku.chocolat.events.VoteStatusEvent;
    import net.fxug.hokuriku.chocolat.model.VoteData;
    import net.fxug.hokuriku.chocolat.utils.LoggingUtil;

    //-------------------------------------------------------------------------
    //
    //  Events
    //
    //-------------------------------------------------------------------------
    /**
     * へぇイベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.GOOD_EVENT
     */
    [Event(name="goodEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]
    /**
     * ぶぅイベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.BAD_EVENT
     */
    [Event(name="badEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]
    /**
     * 失敗イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.FAILED_EVENT
     */
    [Event(name="failedEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]
    /**
     * HTTPステータスイベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteEvent.STATUS_EVENT
     */
    [Event(name="statusEvent", type="net.fxug.hokuriku.chocolat.events.VoteEvent")]

    /**
     * 準備完了イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteStatusEvent.READY_EVENT
     */
    [Event(name="readyEvent", type="net.fxug.hokuriku.chocolat.events.VoteStatusEvent")]
    /**
     * 活性化イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteStatusEvent.ACTIVE_EVENT
     */
    [Event(name="activeEvent", type="net.fxug.hokuriku.chocolat.events.VoteStatusEvent")]
    /**
     * 不活性化イベント
     * @eventType net.fxug.hokuriku.chocolat.events.VoteStatusEvent.INACTIVE_EVENT
     */
    [Event(name="inactiveEvent", type="net.fxug.hokuriku.chocolat.events.VoteStatusEvent")]
    /**
     * 投票処理の総括クラス。シングルトン。
     * このクラスはサーバモードでTimerスタート時にインスタンスを生成するようにしてください。
     *
     * IVoteWatcherを実装するクラスのイベントをこのクラスでプロキシし、View側空の参照先が単一に
     * なる事を目的としたManagerとして利用される事を想定しています。
     *
     * @author hikaruworld
     */
    public class VoteManager extends EventDispatcher
    {
        /** 自身の一意なインスタンス */
        private static var instance:VoteManager;
        /** ロガー */
        private static var logger:ILogger = LoggingUtil.getLogger(VoteManager);
        /**
         * 監視対象のインターフェイス群
         * このプロパティはready()メソッドを実行する前に、設定を行う必要があります。
         * ready()メソッド実行後は読み取り専用になるため、再設定を行うことは出来ません。
         *
         * ここで定義されたそれぞれの実装クラスは内部でDictonaryのKeyとして利用されます。
         */
        private var _targetInterfaces:Vector.<IVoteWatcher>;
        /**
         * インターフェイスの準備完了判定用Map
         * VoteStatusEvent.READY_EVENTをdispatchする際に判定用として参照される。
         */
        private var interfacesReadyMap:Dictionary;
        /**
         * 起動フラグ。
         * 内部的にREADY_EVENTがdispatchされているかを判断する。
         */
        private var isInterfaceReady:Boolean;
        /** DB管理クラス */
        private var databaseManager:DatabaseManager;
        /** 登録対象のセッションID */
        private var sessionId:int;

        //-------------------------------------------------------------------------
        //
        // Class Static Method
        //
        //-------------------------------------------------------------------------
        /**
         * 自身の一意なインスタンスを取得する
         * @return 一意なVoteManagerインスタンス
         */
        public static function getInstance():VoteManager
        {
            if (instance == null)
            {
                instance = new VoteManager(new SingletonBlockr());
            }
            return instance;
        }

        //-------------------------------------------------------------------------
        //
        // Contracture
        //
        //-------------------------------------------------------------------------
        /**
         * コンストラクタ
         * @param blockr 外部からのインスタンス生成抑止用
         * @throws	flash.errors.IllegalOperationError 外部からインスタンス化が行われた場合にthrowされる。
         */
        public function VoteManager(blockr:SingletonBlockr = null)
        {
            if (blockr == null)
            {
                throw new IllegalOperationError("This class cannot instance");
            }
//            this.votedMap = new Dictionary();
            this.interfacesReadyMap = new Dictionary();
            this._targetInterfaces = Vector.<IVoteWatcher>([]);

            this.databaseManager = DatabaseManager.getInstance();
        }

        //-------------------------------------------------------------------------
        //
        // Method
        //
        //-------------------------------------------------------------------------
        /**
         * 投票処理の待ち状態にする。
         * このメソッドは、VoteManagerからdispatchされるVoteStatusEvent.READY_EVENTのリスナとして設定することを推奨します。
         * @param sessionId 登録対象のsessionId
         */
        public function ready(sessionId:int):void
        {
            if (this._targetInterfaces == null || this._targetInterfaces.length == 0)
            {
                throw new IllegalOperationError("_targetInterfaces has not exist...");
            }
            this.sessionId = sessionId;
            this._targetInterfaces.forEach(function(item:IVoteWatcher, index:int, vector:Vector.<IVoteWatcher>):void
                {
                    logger.debug("IVoteWatcher Instance start [{0}]:{1}", item, index);
                    item.start();
                });
        }

        /**
         * 現在有効になっているインターフェイスの一覧を返す。
         * @return 有効な投票管理インターフェイスの一覧。存在しない場合はnullを返す。
         */
        public function enableInterfaces():Vector.<IVoteWatcher>
        {
            if (this._targetInterfaces == null)
            {
                return null;
            }

            var ret:Vector.<IVoteWatcher> = Vector.<IVoteWatcher>([]);
            var index:int = this._targetInterfaces.length;
            for (var i:int = 0; i < index; i++)
            {
                var tmp:IVoteWatcher = this._targetInterfaces[i];
                if (tmp.isEnabled())
                {
                    ret.push(ret);
                }
            }

            return ret;
        }

        /**
         * 投票処理を終了する。
         */
        public function close():void
        {
            this._targetInterfaces.forEach(function(item:IVoteWatcher, index:int, vector:Vector.<IVoteWatcher>):void
                {
                    item.destroy();
                    logger.debug("IVoteWatcher Instance destory [{0}]:{1}", item, index);
                });
            logger.debug("close...");
//            this.dispatchEvent(new VoteStatusEvent(VoteStatusEvent.CLOSE_EVENT, null));
        }

        /**
         * 準備完了を通知します。
         * このメソッドは、IVoteManagerの実装クラス側でインターフェイスの待ち受け準備が完了した時点で実装クラスでかならず実行してください。
         *
         * 注意：このメソッドによってVoteStatusEvent.READY_EVENTをdispatchしますが、
         * すべての実装インターフィエスが準備完了になっていない場合、イベントのdispatchが行われません。
         */
        internal function onReady(key:IVoteWatcher):void
        {
            // 自身のインスタンスが準備完了した事を登録
            // 重複呼び出しが発生している場合はエラーを投げる。
            if (this.interfacesReadyMap[key])
            {
                //TODO カスタムエラー化(重複呼び出しを禁止)
                throw new IllegalStateError("登録しようとした対象インスタンス::" + key + " は既に登録されています。");
            }
            this.interfacesReadyMap[key] = true;

            // 登録されているインターフェイスがすべて準備完了状態になっているか精査
            var resultBool:Boolean = this._targetInterfaces.every(function callback(item:IVoteWatcher, index:int, vector:Vector.<IVoteWatcher>):Boolean
                {
                    return interfacesReadyMap[item];
                }, this);
            if (!resultBool)
            {
                logger.info(key + "::からonReadyが呼び出されましたが、すべての通知クラスの準備が整っていないためVoteStatusEvent.READY_EVENTのdispatchを行いません。");
                return;
            }
            logger.info("VoteManagerの保持するすべての通知クラスの実行準備が整いました");
            this.isInterfaceReady = true;
            var voteStatusEvent:VoteStatusEvent = new VoteStatusEvent(VoteStatusEvent.READY_EVENT, this._targetInterfaces);
            this.dispatchEvent(voteStatusEvent);
        }

        /**
         * 投票処理を発行する。
         * @param voteEvent 投票イベント
         */
        internal function dispatchVote(voteEvent:VoteEvent):void
        {
            // Voetの実行回数をカウントしておく
//            this.votedCntup(voteEvent);
            //TODO つぶやき情報をDBに登録
            var voteArray:Array = voteEvent.tweetArray as Array;

            var baseDate:Date = new Date();

            var voteDaoArray:Array = [];
            var index:int = voteArray.length;
            for (var i:int = 0; i < index; i++)
            {
                var twet:VoteData = voteEvent.tweetArray[i] as VoteData;
                var voteDao:VoteDao = new VoteDao();
                voteDao.datetime = baseDate;
                voteDao.protocol = voteEvent.type == VoteEvent.GOOD_EVENT;
                voteDao.tweet = twet.text;
                voteDao.userId = twet.id;
                voteDao.sessionId = this.sessionId;
                voteDao.route = twet.route;
                voteDaoArray.push(voteDao);
            }
            this.databaseManager.insertMultiVote(voteDaoArray);

            this.dispatchEvent(voteEvent);
        }

        /**
         * 投票管理機構の状態変化を発行する
         * @param voteStatusEvent
         */
        internal function dispatchStatus(voteStatusEvent:VoteStatusEvent):void
        {
            this.dispatchEvent(voteStatusEvent);
        }

        //---------------------------------------------------------------------
        //
        // IDEによる自動生成ロジック
        //
        //---------------------------------------------------------------------
        /**
         * @private
         * @param value Vector.<IVoteWatcher>
         */
        public function set targetInterfaces(value:Vector.<IVoteWatcher>):void
        {
            if (this.isInterfaceReady)
            {
                throw new IllegalStateError("既にサーバ待ち受け状態が活性化されているため、対象インターフェイス一覧を変更する事は出来ません。");
            }
            this._targetInterfaces = value;
        }
    }
}

/**
 * インスタンス生成抑止用
 */
internal class SingletonBlockr
{
}
